#include "Library_t.h"


unsigned int Library_t::numAvailableCopies(const Book_t * book) const{

	return book->getNofAvailableCopies();
}


void Library_t::removeBorrower(ID id){
	map<ISBN,Borrower_t*>::iterator it = borrower_map->find(id);
	if (it != borrower_map->end()){
		Borrower_t* pborrower = it->second;
		while(!pborrower->list_of_books->empty())
		{
			Book_t* pbook = pborrower->list_of_books->back();
			pborrower->list_of_books->pop_back();
			pbook->returnBook(pborrower);
		}
		borrower_map->erase(it);
		delete pborrower;
	}
}

void Library_t::addBorrower(ID id, Name name){
	map<ISBN,Borrower_t*>::iterator it = borrower_map->find(id);
	if (it == borrower_map->end()){
		(*borrower_map)[id] = new Borrower_t(name,id);
	}
}

Borrower_t const * Library_t::searchBorrower(ID id) const{
	return (*borrower_map)[id];
}

void Library_t::addNewCopies(ISBN isbn, unsigned int num_copies, Name name,Author author){
	
	map<ISBN,Book_t*>::iterator it = book_map->find(isbn);

	Book_t* pbook;
	if (it == book_map->end()){ // new book
		pbook = new Book_t(name,author,isbn,num_copies);
		(*book_map)[isbn] = pbook;
	}
	else { // pre-existing book
		pbook = it->second;
		pbook->num_copies += num_copies;
	}
	while (pbook->isAvailable() && !pbook->list_of_waiting->empty()){
		Borrower_t* borrower = pbook->list_of_waiting->back();
		pbook->list_of_waiting->pop_back();
		borrowBook(borrower->getID(),isbn); 
       }

}



bool Library_t::removeCopy(ISBN isbn){

	map<ISBN,Book_t*>::iterator it = book_map->find(isbn);
	
	if (it != book_map->end())
	{
		Book_t* pbook = it->second;
		if (pbook->isAvailable())
		{
			pbook->num_copies--;
			return true;
		}
	}
	return false;
}

bool Library_t::removeAllCopies(ISBN isbn){

	map<ISBN,Book_t*>::iterator it = book_map->find(isbn);
	
	if (it != book_map->end())
	{
		Book_t* pbook = it->second;
		if (pbook->getNofAvailableCopies() == pbook->num_copies)
		{
			book_map->erase(it);
			delete pbook;
			return true;
		}
	}
	return false;
}


bool Library_t::borrowBook(ID id, ISBN isbn){
	Borrower_t* borrower = (*borrower_map)[id];
	if (borrower == 0) return false;

	map<ISBN,Book_t*>::iterator it = book_map->find(isbn);
	if (it == book_map->end()) return false;
	Book_t* pbook = it->second;
	if (pbook != 0 && pbook->borrowBook(borrower)){
		borrower->borrowBook(pbook);
		return true;
	}
	return false;
}

bool Library_t::returnBook(ID id, ISBN isbn){
	Borrower_t * borrower = (*borrower_map)[id];
	if (borrower == 0) return false;

	Book_t* pbook = (*book_map)[isbn];
	if (pbook != 0){
		Borrower_t* new_borrower = pbook->returnBook(borrower);
		if (new_borrower!=0) new_borrower->borrowBook(pbook);
		borrower->returnBook(pbook); 
		return true;
	}
	return false;
}

Library_t::Library_t(){
	book_map = new map<ISBN,Book_t*>();
	borrower_map = new map<ID,Borrower_t*>();
}

Library_t::~Library_t(){
	map<ISBN,Book_t*>::iterator it1 = book_map->begin();
	while(it1 != book_map->end())
	{
		delete it1->second;
		it1++;
	}
	map<ID,Borrower_t*>::iterator it2 = borrower_map->begin();
	while(it2 != borrower_map->end())
	{
		delete it2->second;
		it2++;
	}
	delete book_map;
	delete borrower_map;
}

ostream& operator<<(ostream& os, const Book_t& book)
{
	os << allign<< "Name: " << allign<< book.getName() << endl;
	os << allign<< "Author: " << allign<< book.getAuthor() << endl;
	os << allign<< "ISBN: "<< allign<< book.getISBN() << endl;
	os << allign<< "#Copies: "<< allign<< book.getNofCopies() << endl;
	os << allign<< "#Available: "<< allign<< book.getNofAvailableCopies() << endl;
	return os;
} 
ostream& operator<<(ostream& os, const Borrower_t& borrower) 
{
	os << allign<< "Name: " << allign<< borrower.getName() << endl;
	os << allign<< "ID: " << allign<< borrower.getID() << endl;
	return os;
} 
void Library_t::printBookReport(ISBN isbn) const {
	Book_t* pbook = (*book_map)[isbn];
	if (pbook == 0){
		cout << "Couldn't find book with ISBN: " << isbn << endl;
		return;
	}
	Book_t& book = *pbook;
	cout << endl << "---Book's Properties---" << endl << book;
	if (!book.list_of_borrowers->empty()){
	cout << "Borrowed this book:" << endl;
	for (list<Borrower_t*>::iterator borrower_p_iter = book.list_of_borrowers->begin();
		borrower_p_iter !=  book.list_of_borrowers->end(); borrower_p_iter++)
	{
		cout << **borrower_p_iter << endl;
	}
	}
	if (!book.list_of_waiting->empty()){
	cout << "Waiting for this book:" << endl;
	for (list<Borrower_t*>::iterator borrower_p_iter = book.list_of_waiting->begin();
		borrower_p_iter !=  book.list_of_waiting->end(); borrower_p_iter++)
	{
		cout << **borrower_p_iter << endl;
	}
	}
}

void Library_t::printBorrowerReport(ID id) const
{
	Borrower_t* pborrower = (*borrower_map)[id];
	if (pborrower == 0){
		cout << "Couldn't find pborrower with ID: " << id << endl;
		return;
	}
	Borrower_t& borrower = *pborrower;
	cout << endl << "---Borrower's Properties---" << endl << borrower;
	if (!borrower.list_of_books->empty()){
	cout << "List of items borrowed:" << endl;
	for (list<Book_t*>::iterator book_p_iter = borrower.list_of_books->begin();
		book_p_iter !=  borrower.list_of_books->end(); book_p_iter++)
	{
		cout << (**book_p_iter).getISBN() << endl;
	}
	}
	return;
}

ostream& allign(ostream& os)
{
	return os << left << setw(WIDTH);
}